/*
 * @Author: lineCode 1145938682@qq.com
 * @Date: 2025-08-28 22:42:19
 * @LastEditors: lineCode 1145938682@qq.com
 * @LastEditTime: 2025-11-09 22:30:45
 * @FilePath: /shorthand-go/cmd/generate/main.go
 */
package main

import (
	"flag"
	"fmt"
	"math/rand"
	"os"
	"sort"
	"sync"
	"time"

	"go.uber.org/zap"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
)

const (
	// 题目类型常量
	QuestionTypeMakeSentence     int8 = 0 // 造句
	QuestionTypeWordToDefinition int8 = 1 // 看字选释义
	QuestionTypeDefinitionToWord int8 = 2 // 看释义选字

	// 选项类型常量
	OptionTypeSingleChoice int8 = 0 // 单选

	// 选项数量
	OptionCount int = 4
)

func main() {
	// 定义命令行参数
	var (
		dsn         string
		courseCode  string
		concurrency int
		workerCount int
	)

	flag.StringVar(&dsn, "dsn", "", "MySQL数据库连接字符串")
	flag.StringVar(&courseCode, "course", "", "指定课程code（为空时生成所有课程）")
	flag.IntVar(&concurrency, "concurrency", 5, "并发数（每门课程内的并发组数量）")
	flag.IntVar(&workerCount, "workers", 3, "工作协程数量（同时处理的课程数量）")
	flag.Parse()

	// 验证DSN参数
	if dsn == "" {
		fmt.Fprintf(os.Stderr, "错误: 必须提供数据库连接字符串\n")
		flag.Usage()
		os.Exit(1)
	}

	// 初始化随机数种子
	rand.Seed(time.Now().UnixNano())

	// 初始化日志
	logger, err := initLogger()
	if err != nil {
		fmt.Fprintf(os.Stderr, "初始化日志失败: %v\n", err)
		os.Exit(1)
	}
	defer logger.Sync()

	// 连接数据库
	db, err := initDatabase(dsn, logger)
	if err != nil {
		logger.Fatal("数据库连接失败", zap.Error(err))
	}

	// 获取数据库连接池
	sqlDB, err := db.DB()
	if err != nil {
		logger.Fatal("获取数据库连接池失败", zap.Error(err))
	}
	defer sqlDB.Close()

	// 设置连接池参数
	sqlDB.SetMaxOpenConns(100)
	sqlDB.SetMaxIdleConns(20)

	// 获取要处理的课程列表
	courses, err := getCourses(db, courseCode)
	if err != nil {
		logger.Fatal("获取课程列表失败", zap.Error(err))
	}

	logger.Info("开始生成练习题", zap.Int("course_count", len(courses)), zap.Int("worker_count", workerCount))

	// 使用工作池模式处理课程
	courseChan := make(chan string, len(courses))
	resultChan := make(chan GenerateResult, len(courses))
	wg := sync.WaitGroup{}

	// 启动工作协程
	for i := 0; i < workerCount; i++ {
		wg.Add(1)
		go worker(i, db, courseChan, resultChan, concurrency, &wg, logger)
	}

	// 发送课程到通道
	for _, course := range courses {
		courseChan <- course
	}
	close(courseChan)

	// 等待所有工作完成
	go func() {
		wg.Wait()
		close(resultChan)
	}()

	// 收集结果
	var totalQuestions, totalOptions int
	var successCourses, failedCourses []string

	for result := range resultChan {
		if result.Success {
			totalQuestions += result.QuestionCount
			totalOptions += result.OptionCount
			successCourses = append(successCourses, result.CourseCode)
		} else {
			failedCourses = append(failedCourses, result.CourseCode)
		}
	}

	// 输出统计信息
	logger.Info("练习题生成完成",
		zap.Int("success_courses", len(successCourses)),
		zap.Int("failed_courses", len(failedCourses)),
		zap.Int("total_questions", totalQuestions),
		zap.Int("total_options", totalOptions),
	)

	fmt.Printf("\n生成统计：\n")
	fmt.Printf("成功课程: %d\n", len(successCourses))
	fmt.Printf("失败课程: %d\n", len(failedCourses))
	fmt.Printf("生成题目数: %d\n", totalQuestions)
	fmt.Printf("生成选项数: %d\n", totalOptions)

	if len(failedCourses) > 0 {
		fmt.Printf("\n失败课程列表:\n")
		for _, course := range failedCourses {
			fmt.Printf("- %s\n", course)
		}
	}
}

// GenerateResult 生成结果
type GenerateResult struct {
	CourseCode    string
	Success       bool
	QuestionCount int
	OptionCount   int
}

// worker 工作协程，处理单门课程的练习题生成
func worker(id int, db *gorm.DB, courseChan <-chan string, resultChan chan<- GenerateResult, concurrency int, wg *sync.WaitGroup, logger *zap.Logger) {
	defer wg.Done()

	for courseCode := range courseChan {
		logger.Info("工作协程开始处理课程", zap.Int("worker_id", id), zap.String("course_code", courseCode))

		// 获取课程的所有题目组
		questionGroups, err := getQuestionGroups(db, courseCode)
		if err != nil {
			logger.Error("获取题目组失败", zap.String("course_code", courseCode), zap.Error(err))
			resultChan <- GenerateResult{CourseCode: courseCode, Success: false}
			continue
		}

		// 生成练习题
		result, err := generateCourseQuestions(db, courseCode, questionGroups, concurrency, logger)
		if err != nil {
			logger.Error("生成练习题失败", zap.String("course_code", courseCode), zap.Error(err))
			resultChan <- GenerateResult{CourseCode: courseCode, Success: false}
			continue
		}

		logger.Info("工作协程完成课程处理",
			zap.Int("worker_id", id),
			zap.String("course_code", courseCode),
			zap.Int("question_count", result.QuestionCount),
			zap.Int("option_count", result.OptionCount),
		)

		resultChan <- *result
	}
}

// generateCourseQuestions 生成单门课程的练习题
func generateCourseQuestions(db *gorm.DB, courseCode string, questionGroups []QuestionGroup, concurrency int, logger *zap.Logger) (*GenerateResult, error) {

	// 在生成新题目前，先删除该课程已有的练习题（可选）
	if err := deleteExistingQuestions(db, courseCode); err != nil {
		logger.Error("删除已有练习题失败", zap.String("course_code", courseCode), zap.Error(err))
		return nil, err
	}

	// 使用并发处理题目组
	groupChan := make(chan QuestionGroup, len(questionGroups))
	resultChan := make(chan groupResult, len(questionGroups))
	wg := sync.WaitGroup{}

	// 启动并发处理协程
	for range concurrency {
		wg.Add(1)
		go processQuestionGroup(db, groupChan, resultChan, &wg, logger)
	}

	// 发送题目组到通道
	for _, group := range questionGroups {
		groupChan <- group
	}
	close(groupChan)

	// 等待所有处理完成
	go func() {
		wg.Wait()
		close(resultChan)
	}()

	// 收集结果
	var totalQuestions, totalOptions int
	var hasError bool

	for result := range resultChan {
		if !result.success {
			hasError = true
			logger.Error("处理题目组失败",
				zap.String("course_code", courseCode),
				zap.Int64("group_id", result.groupID),
				zap.Error(result.err),
			)
		} else {
			totalQuestions += result.questionCount
			totalOptions += result.optionCount
		}
	}

	if hasError {
		return nil, fmt.Errorf("部分题目组处理失败")
	}

	return &GenerateResult{
		CourseCode:    courseCode,
		Success:       true,
		QuestionCount: totalQuestions,
		OptionCount:   totalOptions,
	}, nil
}

// groupResult 题目组处理结果
type groupResult struct {
	groupID       int64
	success       bool
	questionCount int
	optionCount   int
	err           error
}

// processQuestionGroup 处理单个题目组，生成练习题
func processQuestionGroup(db *gorm.DB, groupChan <-chan QuestionGroup, resultChan chan<- groupResult, wg *sync.WaitGroup, logger *zap.Logger) {
	defer wg.Done()

	for group := range groupChan {
		questionCount, optionCount, err := generateQuestionsForGroup(db, group, logger)
		resultChan <- groupResult{
			groupID:       group.GroupID,
			success:       err == nil,
			questionCount: questionCount,
			optionCount:   optionCount,
			err:           err,
		}
	}
}

// generateQuestionsForGroup 为单个题目组生成练习题
func generateQuestionsForGroup(db *gorm.DB, group QuestionGroup, logger *zap.Logger) (int, int, error) {
	// 开始事务
	tx := db.Begin()
	defer func() {
		if r := recover(); r != nil {
			tx.Rollback()
			logger.Error("生成练习题发生panic", zap.Any("recover", r))
		}
	}()

	var totalQuestions, totalOptions int

	// 生成三种类型的题目
	for _, content := range group.Contents {
		// 1. 造句题（不需要选项）
		if err := generateMakeSentenceQuestion(tx, group, content); err != nil {
			tx.Rollback()
			return 0, 0, fmt.Errorf("生成造句题失败: %v", err)
		}
		totalQuestions++

		// 2. 看字选释义题
		optionsCount, err := generateWordToDefinitionQuestion(tx, group, content, group.Contents)
		if err != nil {
			tx.Rollback()
			return 0, 0, fmt.Errorf("生成看字选释义题失败: %v", err)
		}
		totalQuestions++
		totalOptions += optionsCount

		// 3. 看释义选字题
		optionsCount, err = generateDefinitionToWordQuestion(tx, group, content, group.Contents)
		if err != nil {
			tx.Rollback()
			return 0, 0, fmt.Errorf("生成看释义选字题失败: %v", err)
		}
		totalQuestions++
		totalOptions += optionsCount
	}

	// 提交事务
	if err := tx.Commit().Error; err != nil {
		return 0, 0, fmt.Errorf("提交事务失败: %v", err)
	}

	logger.Info("题目组生成完成",
		zap.String("course_code", group.CourseCode),
		zap.Int64("group_id", group.GroupID),
		zap.Int("question_count", totalQuestions),
		zap.Int("option_count", totalOptions),
	)

	return totalQuestions, totalOptions, nil
}

// generateMakeSentenceQuestion 生成造句题
func generateMakeSentenceQuestion(tx *gorm.DB, group QuestionGroup, content CourseCNContent) error {
	question := CNQuestion{
		UnitNum:       group.UnitNum,
		CourseCode:    group.CourseCode,
		GroupID:       group.GroupID,
		GroupFlag:     content.GroupFlag,
		OptionType:    OptionTypeSingleChoice,
		QuestionType:  QuestionTypeMakeSentence,
		QuestionTitle: content.CNWord,
		CreateTime:    time.Now(),
		CreateBy:      "system",
		DelFlag:       "0",
	}

	return tx.Create(&question).Error
}

// generateWordToDefinitionQuestion 生成看字选释义题
func generateWordToDefinitionQuestion(tx *gorm.DB, group QuestionGroup, content CourseCNContent, allContents []CourseCNContent) (int, error) {
	// 创建题目
	question := CNQuestion{
		UnitNum:       group.UnitNum,
		CourseCode:    group.CourseCode,
		GroupID:       group.GroupID,
		GroupFlag:     content.GroupFlag,
		OptionType:    OptionTypeSingleChoice,
		QuestionType:  QuestionTypeWordToDefinition,
		QuestionTitle: content.CNWord,
		CreateTime:    time.Now(),
		CreateBy:      "system",
		DelFlag:       "0",
	}

	if err := tx.Create(&question).Error; err != nil {
		return 0, err
	}

	// 生成选项
	options := generateRandomOptions(content.CNParaphrase, getParaphrasePool(allContents, content.ID), OptionCount)

	for _, option := range options {
		questionOption := CNQuestionOption{
			QuestionID:  question.ID,
			OptionValue: option.value,
			RightOption: option.isRight,
			CreateTime:  time.Now(),
			CreateBy:    "system",
			CourseCode:  group.CourseCode,
			Field1:      content.CNPinyin,
		}

		if err := tx.Create(&questionOption).Error; err != nil {
			return 0, err
		}
	}

	return len(options), nil
}

// generateDefinitionToWordQuestion 生成看释义选字题
func generateDefinitionToWordQuestion(tx *gorm.DB, group QuestionGroup, content CourseCNContent, allContents []CourseCNContent) (int, error) {
	// 创建题目
	question := CNQuestion{
		UnitNum:       group.UnitNum,
		CourseCode:    group.CourseCode,
		GroupID:       group.GroupID,
		GroupFlag:     content.GroupFlag,
		OptionType:    OptionTypeSingleChoice,
		QuestionType:  QuestionTypeDefinitionToWord,
		QuestionTitle: content.CNParaphrase,
		CreateTime:    time.Now(),
		CreateBy:      "system",
		DelFlag:       "0",
	}

	if err := tx.Create(&question).Error; err != nil {
		return 0, err
	}

	// 生成选项
	options := generateRandomOptions(content.CNWord, getWordPool(allContents, content.ID), OptionCount)

	for _, option := range options {
		questionOption := CNQuestionOption{
			QuestionID:  question.ID,
			OptionValue: option.value,
			RightOption: option.isRight,
			CreateTime:  time.Now(),
			CreateBy:    "system",
			CourseCode:  group.CourseCode,
		}

		if err := tx.Create(&questionOption).Error; err != nil {
			return 0, err
		}
	}

	return len(options), nil
}

// option 选项结构
type option struct {
	value   string
	isRight int8
}

// generateRandomOptions 生成随机选项
func generateRandomOptions(correctAnswer string, optionPool []string, count int) []option {
	// 确保有足够的选项池
	if len(optionPool) < count-1 {
		count = len(optionPool) + 1
	}

	options := []option{
		{value: correctAnswer, isRight: 1},
	}

	// 随机选择错误选项
	rand.Shuffle(len(optionPool), func(i, j int) {
		optionPool[i], optionPool[j] = optionPool[j], optionPool[i]
	})

	// 添加错误选项，确保不重复
	usedOptions := make(map[string]bool)
	usedOptions[correctAnswer] = true

	for _, opt := range optionPool {
		if !usedOptions[opt] && len(options) < count {
			options = append(options, option{value: opt, isRight: 0})
			usedOptions[opt] = true
		}
		if len(options) >= count {
			break
		}
	}

	// 打乱选项顺序
	rand.Shuffle(len(options), func(i, j int) {
		options[i], options[j] = options[j], options[i]
	})

	return options
}

// getParaphrasePool 获取释义选项池
func getParaphrasePool(contents []CourseCNContent, excludeID int64) []string {
	var pool []string
	for _, content := range contents {
		if content.ID != excludeID && content.CNParaphrase != "" {
			pool = append(pool, content.CNParaphrase)
		}
	}
	return pool
}

// getWordPool 获取汉字选项池
func getWordPool(contents []CourseCNContent, excludeID int64) []string {
	var pool []string
	for _, content := range contents {
		if content.ID != excludeID && content.CNWord != "" {
			pool = append(pool, content.CNWord)
		}
	}
	return pool
}

// getCourses 获取要处理的课程列表
func getCourses(db *gorm.DB, courseCode string) ([]string, error) {
	var courses []string

	if courseCode != "" {
		// 检查课程是否存在
		var count int64
		if err := db.Model(&CourseCNContent{}).Where("course_code = ? AND del_flag = '0'", courseCode).Count(&count).Error; err != nil {
			return nil, err
		}
		if count == 0 {
			return nil, fmt.Errorf("课程 %s 不存在或没有内容", courseCode)
		}
		courses = append(courses, courseCode)
	} else {
		// 获取所有课程
		if err := db.Model(&CourseCNContent{}).Distinct("course_code").Where("del_flag = '0'").Pluck("course_code", &courses).Error; err != nil {
			return nil, err
		}
	}

	return courses, nil
}

// getQuestionGroups 获取题目组列表
func getQuestionGroups(db *gorm.DB, courseCode string) ([]QuestionGroup, error) {
	// 获取单元和集束的映射关系
	unitMap, err := getUnitMap(db, courseCode)
	if err != nil {
		return nil, err
	}

	// 按group_id分组获取内容
	var contents []CourseCNContent
	if err := db.Where("course_code = ? AND del_flag = '0'", courseCode).Find(&contents).Error; err != nil {
		return nil, err
	}

	// 按group_id分组
	groupMap := make(map[int64][]CourseCNContent)
	for _, content := range contents {
		groupMap[content.GroupID] = append(groupMap[content.GroupID], content)
	}

	// 转换为QuestionGroup列表
	var groups []QuestionGroup
	for groupID, groupContents := range groupMap {
		unitNum := 0
		if unitInfo, exists := unitMap[groupID]; exists {
			unitNum = unitInfo.UnitNum
		}

		groups = append(groups, QuestionGroup{
			CourseCode: courseCode,
			GroupID:    groupID,
			UnitNum:    unitNum,
			Contents:   groupContents,
		})
	}

	// 按group_id排序
	sort.Slice(groups, func(i, j int) bool {
		return groups[i].GroupID < groups[j].GroupID
	})

	return groups, nil
}

// getUnitMap 获取单元映射
func getUnitMap(db *gorm.DB, courseCode string) (map[int64]SysUnit, error) {
	var units []SysUnit
	if err := db.Where("course_code = ? AND del_flag = '0'", courseCode).Find(&units).Error; err != nil {
		return nil, err
	}

	unitMap := make(map[int64]SysUnit)
	for _, unit := range units {
		unitMap[unit.GroupID] = unit
	}

	return unitMap, nil
}

// deleteExistingQuestions 删除已有的练习题
func deleteExistingQuestions(db *gorm.DB, courseCode string) error {
	// 开始事务
	tx := db.Begin()

	// 删除选项
	if err := tx.Where("course_code = ?", courseCode).Delete(&CNQuestionOption{}).Error; err != nil {
		tx.Rollback()
		return err
	}

	// 删除题目
	if err := tx.Where("course_code = ?", courseCode).Delete(&CNQuestion{}).Error; err != nil {
		tx.Rollback()
		return err
	}

	return tx.Commit().Error
}

// initLogger 初始化日志
func initLogger() (*zap.Logger, error) {
	config := zap.NewProductionConfig()
	config.OutputPaths = []string{"stdout"}
	config.ErrorOutputPaths = []string{"stderr"}
	return config.Build()
}

// initDatabase 初始化数据库连接
func initDatabase(dsn string, logger *zap.Logger) (*gorm.DB, error) {
	// 使用GORM默认日志配置
	gormConfig := &gorm.Config{}

	db, err := gorm.Open(mysql.Open(dsn), gormConfig)
	if err != nil {
		return nil, err
	}

	return db, nil
}
